package mobile.game.core.gen.proto;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Message;

import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import mobile.game.core.support.ClassFinder;

public class GenProtoMsgId {
	
	/** 模版路径 */
	private static final String TEMPLATE_PATH = "./config/templates";
	
	/** 输出位置 */
	private final String OUTPUT_PATH;
	/** 输出的包名 */
	private final String OUTPUT_PACKAGE;

	private List<MessageInfo> messageInfos = new ArrayList<MessageInfo>();

	protected String pageEncoding = "UTF-8";

	public static void main(String[] args) throws Exception {
		GenProtoMsgId parse = new GenProtoMsgId(args[0], args[1]);
		parse.gen();
		
	}

	/**
	 * 构造器
	 * 
	 * @param outputPath
	 *            输出目录
	 * @param outputPackage
	 *            输出包名(用.分隔的包名称)
	 */
	public GenProtoMsgId(String outputPath, String outputPackage) {
		this.OUTPUT_PATH = outputPath;
		this.OUTPUT_PACKAGE = outputPackage;
	}

	public void gen() throws Exception {
		// 生成临时协议文件,临时文件不以协议区间段开头
		try {
			// 收集Message类
			this.collectMessageClass();
			
			// 生成工具类
			this.genMsgUtil();
		} catch (Exception e) {
			throw e;
		} finally {
			
		}

	}
	
	/**
	 * 收集Message类
	 */
	public void collectMessageClass() {
		List<Class<?>> sources = ClassFinder.getAllClass("common.msg");
		for (Class<?> classz : sources) {
			
			if (Message.class.isAssignableFrom(classz) ) {
				try {
					Method m = classz.getMethod("getDescriptor");
					if (null != m) {
						Descriptor descriptor = (Descriptor)(m.invoke(null));
						int msgid = descriptor.getOptions().getExtension(common.msg.Options.myOption);
						if (0 != msgid) {
							String name =classz.getSimpleName();
							String className = classz.getEnclosingClass().getSimpleName();
							
							MessageInfo mi = new MessageInfo(msgid, name, className);
							System.out.println(mi);
							
							this.messageInfos.add(mi);
						}
					}
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
						| NoSuchMethodException | SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		// 排序
		Collections.sort(this.messageInfos, new Comparator<MessageInfo>() {
			public int compare(MessageInfo a, MessageInfo b) {
				return Integer.compare(a.getId(), b.getId());
			}
		});
	}

	/**
	 * 处理协议描述文件并生成msg工具类
	 */
	@SuppressWarnings("deprecation")
	public void genMsgUtil() throws Exception {

		Configuration cfg = new Configuration();
		TemplateLoader loader = new FileTemplateLoader(new File(TEMPLATE_PATH));
		cfg.setTemplateLoader(loader);
		cfg.setEncoding(Locale.getDefault(), pageEncoding);
		Map<String, Object> valueMap = new HashMap<String, Object>();
		valueMap.put("messageInfos", messageInfos);
		valueMap.put("packageName", this.OUTPUT_PACKAGE);
		Template template = cfg.getTemplate("MsgId.ftl", pageEncoding);
		template.setEncoding(pageEncoding);
		File filePath = new File(this.OUTPUT_PATH, this.OUTPUT_PACKAGE.replace(".", File.separator));
		if (!filePath.exists()) {
			filePath.mkdirs();
		}
		File targetFile = new File(filePath, "MsgIds.java");
		if (targetFile.exists()) {
			targetFile.delete();
		}
		// 合并处理（模板 + 数据模型）
		template.process(valueMap, new OutputStreamWriter(new FileOutputStream(
				targetFile, false), pageEncoding));
	}

	/**
	 * 执行cmd控制台命令
	 * 
	 * @param cmd
	 *            完整命令语句
	 * @param failMsg
	 *            失败时提示语句
	 * @throws Exception
	 *             执行失败
	 */
	@SuppressWarnings("unused")
	private void executeCommand(String cmd, String failMsg) throws Exception {
		// 执行系统命令
		//System.out.println("Execute Command:\n" + cmd);
		Runtime run = Runtime.getRuntime();
		Process p = run.exec(cmd);
		// 如果不正常终止，则生成desc文件失败
		if (p.waitFor() != 0) {
			if (p.exitValue() == 1) {
				//System.err.println(failMsg);

				// 打印输出的错误信息
				BufferedReader in = new BufferedReader(new InputStreamReader(
						p.getErrorStream()));
				String line = "";
				while ((line = in.readLine()) != null) {
					System.out.println(line);
				}
				System.out.println();
			}
		}
	}

}
